import datetime
import time

import pytest
from mock import MagicMock
from mock import patch

from kotti2.resources import File
from kotti2.testing import Dummy
from kotti2.testing import asset
from kotti2.views.cache import set_max_age


def parse_expires(date_string):
    return datetime.datetime(
        *(time.strptime(date_string, "%a, %d %b %Y %H:%M:%S GMT")[0:6])
    )


def delta(date_string):
    now = datetime.datetime.utcnow()
    return parse_expires(date_string) - now


# noinspection PyUnusedLocal
@pytest.fixture
def cachetest_content(root, filedepot):
    image = asset("sendeschluss.jpg")
    root["textfile"] = File(b"file contents", "mytext.txt", "text/plain")
    root["image"] = File(image.read(), "sendeschluss.jpg", "image/jpeg")


class TestSetMaxAge:
    # noinspection PyUnresolvedReferences
    def test_preserve_existing_headers(self):
        response = Dummy(
            headers={"cache-control": "max-age=17,s-max-age=42,foo,bar=42"}
        )
        delta = datetime.timedelta(days=1)
        set_max_age(response, delta)

        assert "cache-control" in response.headers
        assert response.headers["cache-control"] == (
            "bar=42,foo,max-age=86400,s-max-age=42"
        )


class TestSetCacheHeaders:
    def test_caching_policy_chooser(self):
        from kotti2.views.cache import set_cache_headers

        with patch("kotti2.views.cache.caching_policy_chooser") as chooser:
            chooser.return_value = "Random policy"

            event = MagicMock()
            event.response.headers.get.return_value = None

            with pytest.raises(KeyError):
                set_cache_headers(event)

        chooser.assert_called_with(event.request.context, event.request, event.response)

    def test_caching_policy_chooser_raises(self):
        from kotti2.views.cache import set_cache_headers

        def raiser(*args, **kw):
            raise Exception()

        with patch("kotti2.views.cache.caching_policy_chooser") as chooser:
            chooser.side_effect = raiser

            event = MagicMock()
            event.response.headers.get.return_value = None

            with patch("kotti2.views.cache.logger"):
                set_cache_headers(event)

        chooser.assert_called_with(event.request.context, event.request, event.response)

    def test_header_set_before(self):
        from kotti2.views.cache import CACHE_POLICY_HEADER
        from kotti2.views.cache import set_cache_headers

        event = MagicMock()
        event.response.headers = {CACHE_POLICY_HEADER: "Random policy"}

        with patch("kotti2.views.cache.caching_policy_chooser") as chooser:
            with pytest.raises(KeyError):
                set_cache_headers(event)

        assert chooser.call_count == 0

    def test_request_has_no_context(self):
        from kotti2.views.cache import set_cache_headers

        with patch("kotti2.views.cache.caching_policy_chooser") as chooser:
            event = MagicMock()
            event.request = {}
            set_cache_headers(event)

            assert chooser.call_count == 0


class TestBrowser:
    def test_cache_unauth(self, webtest, cachetest_content):

        # html
        resp = webtest.app.get("/")
        assert resp.headers.get("X-Caching-Policy") == "Cache HTML"
        assert resp.headers.get("Cache-Control") == "max-age=0,s-maxage=3600"
        d = delta(resp.headers.get("Expires"))
        assert (d.days, d.seconds) < (0, 0)

        # resources
        resp = webtest.app.get("/static-kotti/base.css")
        assert resp.headers.get("X-Caching-Policy") == "Cache Resource"
        assert resp.headers.get("Cache-Control") == "max-age=2764800,public"
        d = delta(resp.headers.get("Expires"))
        assert (d.days, d.seconds) > (30, 0)
        assert "Last-Modified" in resp.headers

        # post
        resp = webtest.app.post("/", "")
        assert "X-Caching-Policy" not in resp.headers

        # 404
        resp = webtest.app.get("/this-isnt-here", status=404)
        assert "X-Caching-Policy" not in resp.headers

        # media content
        resp = webtest.app.get("/textfile/inline-view")
        assert resp.headers.get("X-Caching-Policy") == "Cache Media Content"
        assert resp.headers.get("Cache-Control") == "max-age=14400,public"
        d = delta(resp.headers.get("Expires"))
        assert (d.days, d.seconds) > (0, 14000)
        resp = webtest.app.get("/image/inline-view")
        assert resp.headers.get("X-Caching-Policy") == "Cache Media Content"
        assert resp.headers.get("Cache-Control") == "max-age=14400,public"
        d = delta(resp.headers.get("Expires"))
        assert (d.days, d.seconds) > (0, 14000)

    @pytest.mark.user("admin")
    def test_cache_auth(self, webtest, cachetest_content):

        # html
        resp = webtest.app.get("/")
        assert resp.headers.get("X-Caching-Policy") == "No Cache"

        # resources
        resp = webtest.app.get("/static-kotti/base.css")
        assert resp.headers.get("X-Caching-Policy") == "Cache Resource"

        # 404
        resp = webtest.app.get("/this-isnt-here", status=404)
        assert "X-Caching-Policy" not in resp.headers

        # media content
        resp = webtest.app.get("/textfile/inline-view")
        assert resp.headers.get("X-Caching-Policy") == "No Cache"
        assert resp.headers.get("Cache-Control") == "max-age=0,public"
        d = delta(resp.headers.get("Expires"))
        assert (d.days, d.seconds) <= (0, 0)
        resp = webtest.app.get("/image/inline-view")
        assert resp.headers.get("X-Caching-Policy") == "No Cache"
        assert resp.headers.get("Cache-Control") == "max-age=0,public"
        d = delta(resp.headers.get("Expires"))
        assert (d.days, d.seconds) <= (0, 0)
